package com.geovis.emergency.spd.biz.pc.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.geovis.common.auth.util.AuthUtil;
import com.geovis.emergency.spd.biz.command.service.ITEpDisasterService;
import com.geovis.emergency.spd.biz.pc.mapper.*;
import com.geovis.emergency.spd.biz.pc.service.IPcDrillDisasterEventService;
import com.geovis.emergency.spd.biz.system.service.ISystemFileBusinessService;
import com.geovis.emergency.spd.biz.system.service.ISystemFileService;
import com.geovis.emergency.spd.cache.DictCache;
import com.geovis.emergency.spd.entity.command.entity.TEpDisaster;
import com.geovis.emergency.spd.entity.pc.entity.*;
import com.geovis.emergency.spd.entity.pc.pojo.constant.PcDisasterConstant;
import com.geovis.emergency.spd.entity.pc.pojo.constant.PcDrillConstant;
import com.geovis.emergency.spd.entity.pc.pojo.constant.PcMessageConstant;
import com.geovis.emergency.spd.entity.pc.pojo.dto.PcDrillDisasterEventDTO;
import com.geovis.emergency.spd.entity.pc.pojo.enums.*;
import com.geovis.emergency.spd.entity.pc.pojo.vo.PcDrillDisasterEventListVO;
import com.geovis.emergency.spd.entity.pc.pojo.vo.PcDrillDisasterEventVO;
import com.geovis.emergency.spd.entity.pc.pojo.vo.PresetEventVO;
import com.geovis.emergency.spd.entity.system.constant.SystemFileBusinessConstant;
import com.geovis.emergency.spd.entity.system.entity.SystemFile;
import com.geovis.emergency.spd.entity.system.entity.SystemFileBusiness;
import com.geovis.emergency.spd.entity.system.pojo.dto.SystemFileDTO;
import com.geovis.emergency.spd.entity.system.pojo.dto.SystemFileQueryDTO;
import com.geovis.emergency.spd.util.MsgUtil;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 应急预案指挥模块-演练灾害事件表 服务实现类
 * </p>
 *
 * @author Lonsho
 * @since 2022-06-08
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class PcDrillDisasterEventServiceImpl extends ServiceImpl<PcDrillDisasterEventMapper, PcDrillDisasterEvent> implements IPcDrillDisasterEventService {

    private final ISystemFileBusinessService fileBusinessService;
    private final PcDrillMapper pcDrillMapper;
    private final PcDrillDisasterMapper pcDrillDisasterMapper;
    private final PcDisasterMapper pcDisasterMapper;

    private final PcDisasterEventMapper pcDisasterEventMapper;

    private final ISystemFileService systemFileService;



    private final PcSchedulingEventStatisticsMapper schedulingEventStatisticsMapper;

    @Override
    public String getEventStep(String drillId){
        List<PcDrillDisasterEvent> pcDrillDisasterEvents = baseMapper.selectList(Wrappers.<PcDrillDisasterEvent>lambdaQuery()
                .eq(PcDrillDisasterEvent::getPcDrillId, drillId)
                .orderByAsc(PcDrillDisasterEvent::getStatus)
                .orderByDesc(PcDrillDisasterEvent::getSubmitTime));
        AtomicInteger index = new AtomicInteger(0);
        for (PcDrillDisasterEvent pcDrillDisasterEvent : pcDrillDisasterEvents) {
            boolean isSubmit = DisasterEventStatusEnum.IN_PROGRESS.getCode().equals(pcDrillDisasterEvent.getStatus()) ||
                    DisasterEventStatusEnum.COMPLETED.getCode().equals(pcDrillDisasterEvent.getStatus());
            if (isSubmit){
                index.incrementAndGet();
            }
        }
        return StrUtil.format("{}/{}",index.get(), pcDrillDisasterEvents.size());
    }
    @Override
    public List<PcDrillDisasterEventListVO> getList(String drillId, String status) {
        PcDrill pcDrill = pcDrillMapper.selectById(drillId);
        Assert.notNull(pcDrill, "演练不存在");
        List<PcDrillDisasterEvent> pcDrillDisasterEvents = baseMapper.selectList(Wrappers.<PcDrillDisasterEvent>lambdaQuery()
                .eq(PcDrillDisasterEvent::getPcDrillId, drillId)
                .in(StrUtil.isNotBlank(status), PcDrillDisasterEvent::getStatus, StrUtil.split(status, ","))
//                .orderByAsc(PcDrillDisasterEvent::getStatus)
//                .orderByDesc(PcDrillDisasterEvent::getActualSubmitTime)
                .orderByDesc(PcDrillDisasterEvent::getSubmitTime));
        List<PcDrillDisasterEventListVO> list = Lists.newArrayList();
        //标识已经有一个待下发了，一个时间线只能有一个待启动的事件
        boolean isHasSubmitOne = false;
        for (int i = pcDrillDisasterEvents.size() - 1; i >= 0; i--) {
            PcDrillDisasterEventListVO eventVO = BeanUtil.toBean(pcDrillDisasterEvents.get(i),PcDrillDisasterEventListVO.class);
            if (StrUtil.isNotBlank(eventVO.getType())){
                String disasterType = DictCache.getDictValue(PcDisasterConstant.DISASTER_TYPE, eventVO.getType());
                if (StrUtil.isNotBlank(disasterType)){
                    eventVO.setTypeName(disasterType);
                }
            }
            if (StrUtil.isNotBlank(eventVO.getLevel())){
                String eventLevel = DictCache.getDictValue(PcDisasterConstant.EVENT_LEVEL, eventVO.getLevel());
                if (StrUtil.isNotBlank(eventLevel)){
                    eventVO.setLevelName(eventLevel);
                }
            }
            if (!isHasSubmitOne){
                //标识是否已经下发了
                boolean isSubmit = DisasterEventStatusEnum.IN_PROGRESS.getCode().equals(eventVO.getStatus()) ||
                        DisasterEventStatusEnum.COMPLETED.getCode().equals(eventVO.getStatus());
                if (i == pcDrillDisasterEvents.size() - 1) {
                    //如果时间线最早的还没启动，则说明整个时间线还没启动
                    if (DisasterEventStatusEnum.TO_BE_ISSUED.getCode().equals(eventVO.getStatus())){
                        //将最早时间线设置为可下发
                        eventVO.setCanSubmit(true);
                        isHasSubmitOne = true;
                    }
                }else {
                    if (!isSubmit){
                        //获取到下一个待启动的事件
                        eventVO.setCanSubmit(true);
                        isHasSubmitOne = true;
                    }
                 }
            }
            //待启动和已经结束的不显示发送按钮
            if (PcDrillConstant.STATUS_COMPLETE.equals(pcDrill.getStatus()) || PcDrillConstant.STATUS_PREPARE.equals(pcDrill.getStatus())){
                eventVO.setCanSubmit(false);
                eventVO.setIsFinish(true);
            }
            PcDrillDisaster pcDrillDisaster = pcDrillDisasterMapper.selectById(eventVO.getPcDrillDisasterId());
            if (ObjectUtil.isNotNull(pcDrillDisaster)){
                eventVO.setEventType(pcDrillDisaster.getEventType());
            }
            list.add(0,eventVO);
        }
        return list;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PcDrillDisasterEvent publishEvent(PcDrillDisasterEventDTO eventDTO) {

        String pcDisasterEventId=eventDTO.getId();
        PcDrillDisasterEvent pcDrillDisasterEvent = baseMapper.selectById(pcDisasterEventId);
        Assert.notNull(pcDrillDisasterEvent,"事件消息【{}】不存在",pcDisasterEventId);

        PcDrill pcDrill = pcDrillMapper.selectById(pcDrillDisasterEvent.getPcDrillId());
        Assert.notNull(pcDrill,"演练不存在");
        Assert.isFalse(PcDrillConstant.STATUS_COMPLETE.equals(pcDrill.getStatus()),"该演练已经结束了");
        Assert.isFalse(PcDrillConstant.STATUS_PREPARE.equals(pcDrill.getStatus()),"该演练尚未开始");

        pcDrillDisasterEvent.setStatus(DisasterEventStatusEnum.IN_PROGRESS.getCode());
        pcDrillDisasterEvent.setActualSubmitTime(LocalDateTime.now());
        //事件下发时，也能修改内容，响应行动等字段
        pcDrillDisasterEvent.setContent(eventDTO.getContent());
        pcDrillDisasterEvent.setSubmitTime(eventDTO.getSubmitTime());
        pcDrillDisasterEvent.setResponseAction(eventDTO.getResponseAction());
        baseMapper.updateById(pcDrillDisasterEvent);

        return pcDrillDisasterEvent;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PcDrillDisasterEvent reportEvent(PcDrillDisasterEventDTO pcDrillDisasterEventDTO,TEpDisaster tepDisaster,Boolean isMobile) {
        PcDrill pcDrill = pcDrillMapper.selectById(pcDrillDisasterEventDTO.getPcDrillId());
        Assert.notNull(pcDrill,"演练不存在");
        Assert.isFalse(PcDrillConstant.STATUS_COMPLETE.equals(pcDrill.getStatus()),"该演练已经结束了");
        Assert.isFalse(PcDrillConstant.STATUS_PREPARE.equals(pcDrill.getStatus()),"该演练尚未开始");
        PcDrillDisasterEvent pcDrillDisasterEvent = BeanUtil.toBean(pcDrillDisasterEventDTO,PcDrillDisasterEvent.class);
        //todo 这里需要讨论，目前这里没有生成pc_disaster和pc_disaster_event的记录，所以这里把值班值守的id设置进去不知道对不对
        pcDrillDisasterEvent.setPcDisasterEventId(pcDrillDisasterEvent.getId());
        pcDrillDisasterEvent.setId(null);
        pcDrillDisasterEvent.setStatus(DisasterEventStatusEnum.IN_PROGRESS.getCode());
        //上报时间为页面表单上的上报时间
//        pcDrillDisasterEvent.setSubmitTime(LocalDateTime.now());
        pcDrillDisasterEvent.setActualSubmitTime(LocalDateTime.now());
        pcDrillDisasterEvent.setSchedulingEventId(pcDrillDisasterEventDTO.getSchedulingEventId());
        if (StrUtil.isNotBlank(pcDrillDisasterEventDTO.getEventTypeCode())){
            pcDrillDisasterEvent.setType(pcDrillDisasterEventDTO.getEventTypeCode());
        }
        if (StrUtil.isNotBlank(pcDrillDisasterEventDTO.getEventLevelCode())){
            pcDrillDisasterEvent.setLevel(pcDrillDisasterEventDTO.getEventLevelCode());
        }
        //新需求 预设事件选择某个消息提交之后，应该要插入对应演练事件PcDrillDisaster，先判断是否已存在，一个预设事件会有多条消息
        if(StrUtil.isNotBlank(pcDrillDisasterEventDTO.getPcDisasterId()))
        {
            PcDrillDisaster pcDrillDisaster=pcDrillDisasterMapper.selectOne(Wrappers.lambdaQuery(PcDrillDisaster.class)
                    .eq(PcDrillDisaster::getPcDrillId, pcDrill.getId())
                    .eq(PcDrillDisaster::getPcDisasterId, pcDrillDisasterEventDTO.getPcDisasterId()));
            if(pcDrillDisaster==null)
            {
                pcDrillDisaster=saveNewDrillDisaster(pcDrillDisasterEventDTO,pcDrill,SourceTypeEnum.PRESET_EVENT.getCode());
            }
            pcDrillDisasterEvent.setPcDrillDisasterId(pcDrillDisaster.getId());
        }
        else
        {
            //移动端提交，eventId为null，也不会有值班值守事件，所以tepDisaster也为null
            //如果选择了续报事件，且关联某个事件，则不生生成新的pc_drill_disaster事件
            if (StrUtil.isNotBlank(pcDrillDisasterEventDTO.getEventId())){
                pcDrillDisasterEvent.setPcDrillDisasterId(pcDrillDisasterEventDTO.getEventId());
            } else {
                String sourceType=isMobile? SourceTypeEnum.MOBILE_NEW_EVENT.getCode():SourceTypeEnum.NEW_EVENT.getCode();
                //新事件上报
                PcDrillDisaster pcDrillDisaster=saveNewDrillDisaster(pcDrillDisasterEventDTO,pcDrill,sourceType);
                pcDrillDisasterEvent.setPcDrillDisasterId(pcDrillDisaster.getId());
            }
        }
        baseMapper.insert(pcDrillDisasterEvent);
        //如果是指挥的演练，推送了值班值守的事件，则插入一些统计信息
        if(null!=pcDrillDisasterEvent.getSchedulingEventId())
        {
            saveEventStat(pcDrillDisasterEvent.getSchedulingEventId(),pcDrillDisasterEvent.getPcDrillId(),pcDrillDisasterEvent.getPcDrillDisasterId(),tepDisaster);
        }

        //保存附件
        if(CollUtil.isNotEmpty(pcDrillDisasterEventDTO.getFileList())){
            Set<SystemFileBusiness> fileBusinessSet = CollUtil.set(false);
            for (SystemFile systemFile : pcDrillDisasterEventDTO.getFileList()) {
                fileBusinessSet.add(new SystemFileBusiness(systemFile.getId(), SystemFileBusinessConstant.TYPE_PC_DRILL_DISASTER_EVENT, pcDrillDisasterEvent.getId()));
            }
            fileBusinessService.saveBatch(fileBusinessSet);
        }
        return pcDrillDisasterEvent;
    }

    /**
     * 保存值班值守的事件的统计信息
     * @param eventId
     * @param drillId
     * @param pcDrillDisasterId
     */
    private void saveEventStat(String eventId,String drillId,String pcDrillDisasterId,TEpDisaster tepDisaster)
    {
//        TEpDisaster tepDisaster = tepDisasterService.getById(eventId);
        if(tepDisaster==null)
        {
            return;
        }
        PcSchedulingEventStatistics pcSchedulingEventStatistics = new PcSchedulingEventStatistics();
        pcSchedulingEventStatistics.setDrillId(drillId);
        pcSchedulingEventStatistics.setPcDrillDisasterId(pcDrillDisasterId);
        pcSchedulingEventStatistics.setType(EventStatisticsTypeEnum.SCHEDULING_EVENT.getCode());
        pcSchedulingEventStatistics.setDeathNum(tepDisaster.getDeathNum());
        pcSchedulingEventStatistics.setMissNum(tepDisaster.getMissNum());
        pcSchedulingEventStatistics.setInjuredNum(tepDisaster.getInjuredNum());
        schedulingEventStatisticsMapper.insert(pcSchedulingEventStatistics);
    }

    private PcDrillDisaster saveNewDrillDisaster(PcDrillDisasterEventDTO pcDrillDisasterEventDTO,PcDrill pcDrill,String sourceType){
        //新事件上报
        //todo 讨论这里是否要生成新的突发事件和事件消息记录pc_disaster，pc_disaster_event
        PcDrillDisaster pcDrillDisaster = new PcDrillDisaster();
        pcDrillDisaster.setSourceType(sourceType);
        pcDrillDisaster.setPcDrillId(pcDrillDisasterEventDTO.getPcDrillId());
        pcDrillDisaster.setType(pcDrillDisasterEventDTO.getType());
        if (StrUtil.isNotBlank(pcDrillDisasterEventDTO.getEventTypeCode())){
            pcDrillDisaster.setType(pcDrillDisasterEventDTO.getEventTypeCode());
        }
        pcDrillDisaster.setDataStatus(StatusEnum.TRUE.getCode());
        pcDrillDisaster.setEventType(EventTypeEnum.BURST_EVENT.getCode());
        Long count = pcDrillDisasterMapper.selectCount(Wrappers.lambdaQuery(PcDrillDisaster.class).eq(PcDrillDisaster::getPcDrillId, pcDrill.getId()));
        pcDrillDisaster.setIdx(count.intValue()+1);
        if(StringUtils.hasText(pcDrillDisasterEventDTO.getTitle()))
        {
            pcDrillDisaster.setName(pcDrillDisasterEventDTO.getTitle());
        }
        else
        {
            Long newCount = pcDrillDisasterMapper.selectCount(Wrappers.lambdaQuery(PcDrillDisaster.class).eq(PcDrillDisaster::getPcDrillId, pcDrill.getId()).likeRight(PcDrillDisaster::getName, "新建事件"));
            pcDrillDisaster.setName("新建事件"+(newCount.intValue()+1));
        }
        pcDrillDisasterMapper.insert(pcDrillDisaster);
        return pcDrillDisaster;
    }
    @Override
    public void saveNewEvent(PcSchedulingEvent pcSchedulingEvent,PcDrill pcDrill){
        PcDrillDisaster pcDrillDisaster = new PcDrillDisaster();
        pcDrillDisaster.setSourceType(SourceTypeEnum.NEW_EVENT.getCode());
        pcDrillDisaster.setPcDrillId(pcDrill.getId());
        pcDrillDisaster.setDataStatus(StatusEnum.TRUE.getCode());
        pcDrillDisaster.setEventType(EventTypeEnum.MAIN_EVENT.getCode());
        pcDrillDisaster.setIdx(0);
        pcDrillDisaster.setName(pcSchedulingEvent.getTitle());
        pcDrillDisaster.setType(pcSchedulingEvent.getType());
        pcDrillDisasterMapper.insert(pcDrillDisaster);

        PcDrillDisasterEvent pcDrillDisasterEvent = BeanUtil.toBean(pcSchedulingEvent,PcDrillDisasterEvent.class);
        pcDrillDisasterEvent.setPcDrillDisasterId(pcDrillDisaster.getId());
        pcDrillDisasterEvent.setPcDrillId(pcDrill.getId());
        pcDrillDisasterEvent.setSource(pcSchedulingEvent.getSubmitDept());
        pcDrillDisasterEvent.setEventStage(EventStageEnum.BEFORE_DISASTER.getCode());
        pcDrillDisasterEvent.setResponseType(ResponseTypeEnum.BROADCAST_EVENT.getCode());
        pcDrillDisasterEvent.setSourceType(SourceTypeEnum.NEW_EVENT.getCode());
        pcDrillDisasterEvent.setStatus(DisasterEventStatusEnum.IN_PROGRESS.getCode());
        pcDrillDisasterEvent.setActualSubmitTime(LocalDateTime.now());
        save(pcDrillDisasterEvent);
    }

    @Override
    public PresetEventVO getNextEvent(String drillId, String disasterId) {
        //先获取当前的已经发送过预设事件
        Long current = baseMapper.selectCount(
                Wrappers.lambdaQuery(PcDrillDisasterEvent.class)
                        .eq(PcDrillDisasterEvent::getPcDrillId, drillId)
                        .eq(PcDrillDisasterEvent::getPcDisasterId, disasterId)
        );
        List<PcDisasterEvent> pcDisasterEvents = pcDisasterEventMapper.selectList(
                Wrappers.lambdaQuery(PcDisasterEvent.class)
                        .eq(PcDisasterEvent::getPcDisasterId, disasterId)
                        .orderByAsc(PcDisasterEvent::getSubmitTime)
        );
        Assert.notEmpty(pcDisasterEvents, "该灾害时间没有配置事件，请配置事件后再重试");
        PresetEventVO presetEventVO = new PresetEventVO();

        if(current>=pcDisasterEvents.size())
        {
            return presetEventVO;
        }
        long currentSize=current+1;

        presetEventVO.setStep(StrUtil.format("{}/{}",currentSize,pcDisasterEvents.size()));

        if (current < pcDisasterEvents.size()){
            PcDisasterEvent pcDisasterEvent = pcDisasterEvents.get(current.intValue());
            PcDrillDisasterEventVO pcDrillDisasterEventVO = BeanUtil.toBean(pcDisasterEvent,PcDrillDisasterEventVO.class);
            setDetailExtra(pcDrillDisasterEventVO, SystemFileBusinessConstant.TYPE_PC_DISASTER_EVENT);
            presetEventVO.setDisasterEventVO(pcDrillDisasterEventVO);
        }
        return presetEventVO;
    }
    public void setDetailExtra(PcDrillDisasterEventVO pcDrillDisasterEventVO,String type){
        //灾害类型
        if (StrUtil.isNotBlank(pcDrillDisasterEventVO.getType())){
            String dictValue = DictCache.getDictValue(PcDisasterConstant.DISASTER_TYPE, pcDrillDisasterEventVO.getType());
            pcDrillDisasterEventVO.setTypeName(dictValue);
        }
        //事件等级
        if (StrUtil.isNotBlank(pcDrillDisasterEventVO.getLevel())){
            String dictValue = DictCache.getDictValue(PcDisasterConstant.EVENT_LEVEL, pcDrillDisasterEventVO.getLevel());
            pcDrillDisasterEventVO.setLevelName(dictValue);
        }

        //附件信息
        pcDrillDisasterEventVO.setFileList(
                systemFileService.getListById(pcDrillDisasterEventVO.getId(),type)
        );
    }

    @Override
    public List<String> listIdByDrillIdAndEventStage(String pcDrillId, String eventStage) {
        return pcDisasterEventMapper.listIdByDrillIdAndEventStage(pcDrillId,eventStage);
    }
}
